package com.codeiy.criteria;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.Data;

import java.io.Serializable;
import java.util.List;
import java.util.stream.Collectors;

@Data
public class CriteriaQuery implements Serializable {
    private List<Criteria> criteriaList;
    private List<OrderBy> orderByList;
    private Page page;

    public QueryWrapper toQueryWrapper() {
        QueryWrapper<Object> wrapper = Wrappers.query();
        if (CollectionUtil.isNotEmpty(criteriaList)) {
            criteriaList.forEach(criteria -> {
                String fieldName = StrUtil.toUnderlineCase(criteria.getField());
                String value = criteria.getValue();
                if (StrUtil.isBlank(fieldName) || StrUtil.isBlank(value)) {
                    return;
                }
                if (Relation.OR.equals(criteria.getRelation()) && criteria.getField().split(Criteria.SEPARATOR).length > 1) {
                    String[] fields = criteria.getField().split(Criteria.SEPARATOR);
                    switch (criteria.getPredicate()) {
                        case LIKE:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).like(true, StrUtil.toUnderlineCase(field), value);
                                }
                            });
                            break;
                        case EQ:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).eq(true, StrUtil.toUnderlineCase(field), value);
                                }
                            });
                            break;
                        case GE:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).ge(true, StrUtil.toUnderlineCase(field), value);
                                }
                            });
                            break;
                        case GT:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).gt(true, StrUtil.toUnderlineCase(field), value);
                                }
                            });
                            break;
                        case IN:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).in(true, StrUtil.toUnderlineCase(field), value.split(","));
                                }
                            });
                            break;
                        case LE:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).le(true, StrUtil.toUnderlineCase(field), value);
                                }
                            });
                            break;
                        case LT:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).lt(true, StrUtil.toUnderlineCase(field), value);
                                }
                            });
                            break;
                        case NE:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).ne(true, StrUtil.toUnderlineCase(field), value);
                                }
                            });
                            break;
                        case NULL:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).isNull(true, StrUtil.toUnderlineCase(field));
                                }
                            });
                            break;
                        case NOT_IN:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).notIn(true, StrUtil.toUnderlineCase(field), value.split(","));
                                }
                            });
                            break;
                        case NOT_LIKE:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).notLike(true, StrUtil.toUnderlineCase(field), value);
                                }
                            });
                            break;
                        case NOT_NULL:
                            wrapper.and(true, w -> {
                                for (String field : fields) {
                                    w.or(true).isNotNull(true, StrUtil.toUnderlineCase(field));
                                }
                            });
                            break;
                        default:
                            break;
                    }
                } else {
                    switch (criteria.getPredicate()) {
                        case LIKE:
                            wrapper.like(fieldName, value);
                            break;
                        case EQ:
                            wrapper.eq(fieldName, value);
                            break;
                        case GE:
                            wrapper.ge(fieldName, value);
                            break;
                        case GT:
                            wrapper.gt(fieldName, value);
                            break;
                        case IN:
                            wrapper.in(fieldName, value.split(","));
                            break;
                        case LE:
                            wrapper.le(fieldName, value);
                            break;
                        case LT:
                            wrapper.lt(fieldName, value);
                            break;
                        case NE:
                            wrapper.ne(fieldName, value);
                            break;
                        case NULL:
                            wrapper.isNull(fieldName);
                            break;
                        case NOT_IN:
                            wrapper.notIn(fieldName, value.split(","));
                            break;
                        case NOT_LIKE:
                            wrapper.notLike(fieldName, value);
                            break;
                        case NOT_NULL:
                            wrapper.isNotNull(fieldName);
                            break;
                        default:
                            break;
                    }
                }
            });
        }
        if (CollectionUtil.isNotEmpty(orderByList)) {
            orderByList.forEach(orderBy -> {
                String field = StrUtil.toUnderlineCase(orderBy.getField());
                if (StrUtil.isBlank(field) || StrUtil.isBlank(orderBy.getOrder())) {
                    return;
                }
                if (OrderBy.DESC.equalsIgnoreCase(orderBy.getOrder())) {
                    wrapper.orderByDesc(field);
                } else {
                    wrapper.orderByAsc(field);
                }
            });
        }
        return wrapper;
    }

    public com.baomidou.mybatisplus.extension.plugins.pagination.Page toMybatisPlusPage() {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page mybatisPlusPage = new com.baomidou.mybatisplus.extension.plugins.pagination.Page();
        if (page != null) {
            mybatisPlusPage.setCurrent(page.getPageIndex());
            mybatisPlusPage.setSize(page.getPageSize());
        }
        return mybatisPlusPage;
    }

    public String getProjectCode() {
        return criteriaList.stream().filter(c -> "projectCode".equals(c.getField())).map(Criteria::getValue).collect(Collectors.joining());
    }

    public String getObjectCode() {
        return criteriaList.stream().filter(c -> "objectCode".equals(c.getField())).map(Criteria::getValue).collect(Collectors.joining());
    }

    public static class Builder {
        private List<Criteria> criteriaList;
        private List<OrderBy> orderByList;
        private Page page;

        public static Builder of(String field, Predicate predicate, String value) {
            Builder builder = new Builder();
            builder.criteriaList = ListUtil.of(new Criteria(field, predicate, value, Relation.AND));
            return builder;
        }

        public static Builder of(String field, String order) {
            Builder builder = new Builder();
            builder.orderByList = ListUtil.of(new OrderBy(field, order));
            return builder;
        }

        public static Builder of(Integer pageSize, Integer pageIndex) {
            Builder builder = new Builder();
            builder.page = new Page(pageSize, pageIndex);
            return builder;
        }

        public CriteriaQuery build() {
            CriteriaQuery criteriaQuery = new CriteriaQuery();
            criteriaQuery.criteriaList = this.criteriaList;
            criteriaQuery.orderByList = this.orderByList;
            criteriaQuery.page = this.page;
            return criteriaQuery;
        }

    }
}
